/* Copyright 2008 Bas van den Berg
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <string>

#include "yaffut.h"
#include "myassert.h"
#include "TestSession.h"
#include "TestEventServer.h"
#include "TestRoom.h"

#include "MudContainer.h"
#include "MudCreature.h"
#include "MudExit.h"
#include "MudItem.h"
#include "MudCredits.h"
#include "MudObject.h"
#include "MudPlayer.h"
#include "MudWearable.h"
#include "Parser.h"
#include "ParserFactory.h"
#include "color.h"
#include "Trade.h"

#include "Commands.h"

using namespace std;
using namespace Chronos;


class TradeCommandsTest {
public:
    TradeCommandsTest()
        : parser(*ParserFactory::create())
		, room("RoomName")
        , session1("player1")
        , session2("player2")
        , player1(session1.getPlayer())
        , player2(session2.getPlayer())
    {
		room.addPlayer(player1, "");
		room.addPlayer(player2, "");
        session1.clear();
        session2.clear();
        room.clear();
    }
    ~TradeCommandsTest() {
        if (player1.inTrade()) player1.getTrade()->cancel();
		room.removePlayer(player1, "");
		room.removePlayer(player2, "");
        delete &parser;
    }
	void checkResult(TestSession& session, const char* input, const string& expected) {
		parser.parse(session, input);
		session.SESSION_CHECKWRITTEN(expected);
	}

    Parser& parser;
    TestRoom room;

    TestSession session1;
    TestSession session2;
    MudPlayer& player1;
    MudPlayer& player2;
};


TEST(TradeCommandsTest, testTradeCommandsWithoutTrade) {
    checkResult(session1, "trade stop", "You're not in a trade.\n");
    checkResult(session1, "trade accept", "You're not in a trade.\n");
    checkResult(session1, "trade add item", "You're not in a trade.\n");
    checkResult(session1, "trade credits 1000", "You're not in a trade.\n");
    checkResult(session1, "trade stop", "You're not in a trade.\n");
    checkResult(session1, "trade show", "You're not in a trade.\n");
    checkResult(session1, "trade see item", "You're not in a trade.\n");
}


TEST(TradeCommandsTest, testStartingTrade) {
    checkResult(session1, "trade with player1", "You cannot trade with yourself.\n");
    checkResult(session1, "trade with unknown player", "There is no unknown player here.\n");
    
    room.addObject(new MudItem(0, "item", "", 1));
    checkResult(session1, "trade with item", "You cannot trade with that!\n");

    checkResult(session1, "trade with player2", "You start a trade with player2.\n");
    session2.SESSION_CHECKWRITTEN("player1 starts a trade with you.\n");

    checkResult(session1, "trade with player2", "You are already in a trade.\n");
}


class TradeCommandsTest2 {
public:
    TradeCommandsTest2()
        : parser(*ParserFactory::create())
		, room("RoomName")
        , session1("player1")
        , session2("player2")
        , player1(session1.getPlayer())
        , player2(session2.getPlayer())
    {
		room.addPlayer(player1, "");
		room.addPlayer(player2, "");
        parser.parse(session1, "trade with player2");
        session1.clear();
        session2.clear();
        room.clear();
    }
    ~TradeCommandsTest2() {
        if (player1.inTrade()) player1.getTrade()->cancel();
		room.removePlayer(player1, "");
		room.removePlayer(player2, "");
        delete &parser;
    }
	void checkResult(TestSession& session, const char* input, const string& expected) {
		parser.parse(session, input);
		session.SESSION_CHECKWRITTEN(expected);
	}

    Parser& parser;
    TestRoom room;

    TestSession session1;
    TestSession session2;
    MudPlayer& player1;
    MudPlayer& player2;
};


TEST(TradeCommandsTest2, testShowCommandWithCredits) {
    player1.setCredits(50);
    parser.parse(session1, "trade credits 50");
    player2.addItem(new MudItem(0, "item", "", 1));
    parser.parse(session2, "trade add item");
    session1.clear();
    checkResult(session1, "trade show",
        "player1     <-->  player2\n"
        "50 credits        \n"
        "                  item\n"
    );
}


TEST(TradeCommandsTest2, testAddingShouldResetAccepts) {
    checkResult(session1, "trade accept", "OK\n");
    
    player1.addItem(new MudItem(0, "item1", "", 1));
    checkResult(session1, "trade add item1", "OK\n");
    session2.clear();

    checkResult(session2, "trade accept", "OK\n");
}


TEST(TradeCommandsTest2, testStartingTradeWithOtherInTrade) {
    TestSession session3("player3");
	room.addPlayer(session3.getPlayer(), "");
    session3.clear();
    checkResult(session3, "trade with player2", "player2 is already in trade.\n");
	room.removePlayer(session3.getPlayer(), "");
}


TEST(TradeCommandsTest2, testStoppingTradeAndReStarting) {
    checkResult(session1, "trade stop", "trade cancelled.\n");
    session2.SESSION_CHECKWRITTEN("trade cancelled.\n");

    checkResult(session2, "trade with player1", "You start a trade with player1.\n");
    session1.SESSION_CHECKWRITTEN("player2 starts a trade with you.\n");
}


TEST(TradeCommandsTest2, testAddingItems) {
    MudWearable* equip = new MudWearable(0, "equip", "", 1, Equipment::head);
	player1.addItem(equip);
    player1.wear(equip);
    session1.clear();
    checkResult(session1, "trade add equip", "Please remove it first.\n");

    player1.addItem(new MudItem(0, "item1", "", 1));
	ASSERT_NOT_NULL(player1.findItem("item1"));
    checkResult(session1, "trade add item1", "OK\n");
	ASSERT_NULL(player1.findItem("item1"));
    session2.SESSION_CHECKWRITTEN("player1 adds item1 to trade.\n");
    checkResult(session1, "trade add item1", "You don't have a item1.\n");

    player2.addItem(new MudItem(0, "item2", "", 1));
	ASSERT_NOT_NULL(player2.findItem("item2"));
    checkResult(session2, "trade add item2", "OK\n");
	ASSERT_NULL(player2.findItem("item2"));
    session1.SESSION_CHECKWRITTEN("player2 adds item2 to trade.\n");
}


TEST(TradeCommandsTest2, testAddingCredits) {
    player1.setCredits(50);
    checkResult(session1, "trade credits 100", "You don't have that many credits.\n");
    checkResult(session1, "trade credits -100", "You cannot trade negative amounts.\n");
    
    checkResult(session1, "trade credits 30", "OK\n");
    session2.SESSION_CHECKWRITTEN("player1 adds 30 credits to trade.\n");
    ASSERT_EQUAL(20, player1.getCredits());

    player2.setCredits(100);
    checkResult(session2, "trade credits 100", "OK\n");
    session1.SESSION_CHECKWRITTEN("player2 adds 100 credits to trade.\n");
    ASSERT_EQUAL(0, player2.getCredits());
}


class TradeCommandsTest3 {
public:
    TradeCommandsTest3()
        : parser(*ParserFactory::create())
		, room("RoomName")
        , session1("player1")
        , session2("player2")
        , player1(session1.getPlayer())
        , player2(session2.getPlayer())
    {
		room.addPlayer(player1, "");
		room.addPlayer(player2, "");
        parser.parse(session1, "trade with player2");

        player1.setCredits(50);
        player1.addItem(new MudItem(0, "item1a", "", 1));
        player1.addItem(new MudItem(0, "item1b", "", 1));
        parser.parse(session1, "trade add item1a");
        parser.parse(session1, "trade add item1b");
        parser.parse(session1, "trade credits 50");

        player2.setCredits(100);
        player2.addItem(new MudItem(0, "item2a", "", 1));
        player2.addItem(new MudItem(0, "item2b", "", 1));
        parser.parse(session2, "trade add item2a");
        parser.parse(session2, "trade add item2b");
        parser.parse(session2, "trade credits 100");
        session1.clear();
        session2.clear();
        room.clear();
    }
    ~TradeCommandsTest3() {
        if (player1.inTrade()) player1.getTrade()->cancel();
		room.removePlayer(player1, "");
		room.removePlayer(player2, "");
        delete &parser;
    }
	void checkResult(TestSession& session, const char* input, const string& expected) {
		parser.parse(session, input);
		session.SESSION_CHECKWRITTEN(expected);
	}

    Parser& parser;
    TestRoom room;

    TestSession session1;
    TestSession session2;
    MudPlayer& player1;
    MudPlayer& player2;
};


TEST(TradeCommandsTest3, testShowCommand) {
    checkResult(session1, "trade show",
        "player1     <-->  player2\n"
        "50 credits        100 credits\n"
        "item1a            item2a\n"
        "item1b            item2b\n"
    );
}


TEST(TradeCommandsTest3, testStoppingTradeShouldReturnItems) {
	checkResult(session1, "trade stop", "trade cancelled.\n");
    session2.SESSION_CHECKWRITTEN("trade cancelled.\n");

    ASSERT_NOT_NULL(player1.findItem("item1a"));
    ASSERT_NOT_NULL(player1.findItem("item1b"));
    ASSERT_EQUAL(50, player1.getCredits());
	ASSERT_NOT_NULL(player2.findItem("item2a"));
	ASSERT_NOT_NULL(player2.findItem("item2b"));
    ASSERT_EQUAL(100, player2.getCredits());
}


TEST(TradeCommandsTest3, testSuccessfulTrade) {
	checkResult(session1, "trade accept", "OK\n");
    session2.SESSION_CHECKWRITTEN("player1 has accepted the trade.\n");

    checkResult(session2, "trade accept", "trade finished.\nYou receive a item1a\nYou receive a item1b\nYou receive 50 credits\n");
    session1.SESSION_CHECKWRITTEN("trade finished.\nYou receive a item2a\nYou receive a item2b\nYou receive 100 credits\n");

    ASSERT_NOT_NULL(player2.findItem("item1a"));
    ASSERT_NOT_NULL(player2.findItem("item1b"));
    ASSERT_EQUAL(50, player2.getCredits());
	ASSERT_NOT_NULL(player1.findItem("item2a"));
	ASSERT_NOT_NULL(player1.findItem("item2b"));
    ASSERT_EQUAL(100, player1.getCredits());
}


